﻿using System;
using System.Diagnostics;

namespace calculator
{
    class Scanner : IScanner
    {


        public Scanner(string buf)
        {
            _buf = buf;
            _iLook = 0;
            Console.WriteLine("Scanner with {0}.", _buf);
            Accept();
        }

        public EToken Token()
        {
            return _token;
        }
        /// <summary>
        /// Moves the scanner to the next Token is the input
        /// </summary>
        /// <returns></returns>
        public EToken Accept()
        {
            if (_iLook >= _buf.Length - 1)
                return EToken.End;
            EatWhite();
            switch (_buf[_iLook])
            {
                case '+':
                    _token = EToken.Plus;
                    ++_iLook;
                    break;
                case '*':
                    _token = EToken.Mult;
                    ++_iLook;
                    break;
                case '-':
                    _token = EToken.Minus;
                    ++_iLook;
                    break;
                case '/':
                    _token = EToken.Divide;
                    ++_iLook;
                    break;
                case '(':
                    _token = EToken.LParen;
                    ++_iLook;
                    break;
                case ')':
                    _token = EToken.RParen;
                    ++_iLook;
                    break;
                case '=':
                    _token = EToken.Assign;
                    ++_iLook;
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '.':
                    _token = EToken.Number;
                    int i = _iLook;
                    int count = 1;
                    while (char.IsDigit(_buf[_iLook]))
                    {
                        ++_iLook;
                        ++count;
                        if (_iLook >= _buf.Length - 1)
                            return EToken.End;
                    }
                    Debug.Assert(_iLook < _buf.Length - 1);
                    _number = double.Parse(_buf.Substring(i, count));
                    break;
                case '\0':
                    _token = EToken.End;
                    break;
                default:
                    if (char.IsLetter(_buf[_iLook]) || _buf[_iLook] == '_')
                    {
                        _token = EToken.Ident;
                        _iSym = _iLook;
                        char cLook;
                        do
                        {
                            ++_iLook;
                            cLook = _buf[_iLook];
                        }
                        while (char.IsLetterOrDigit(cLook) || cLook == '_');

                        _lenSymbol = _iLook - _iSym;
                        if (_lenSymbol > maxSymLen)
                            _lenSymbol = maxSymLen;
                    }
                    else
                        _token = EToken.Error;
                    break;
            }
            return Token();
        }

        public double Number()
        {
            Debug.Assert(_token == EToken.Number);
            return _number;
        }

        public void SymbolName(out string str, out int len)
        {
            //Debug.Assert(len >= maxSymLen);
            Debug.Assert(_lenSymbol <= maxSymLen);
            str = _buf.Substring(_iSym, _lenSymbol);
            len = _lenSymbol;
        }

        private readonly string _buf;
        private int _iLook;
        private int _iSym;
        private EToken _token;
        private double _number;
        private const int maxSymLen = 4;
        private int _lenSymbol;
        private void EatWhite()
        {
            while (char.IsWhiteSpace(_buf[_iLook]))
            {
                ++_iLook;
            }
        }
    }
}
